scriptSupport.addInit(function() {
	new EventListener(document, "mouseup", function(evn) {
		if (this._resize) {
			this._resize = null;
			return EventListener.cancelEvent(evn);
		}
	});

	new EventListener(document, "mousemove", function(evn) {
		var s = Wnd(this).getInnerSize();
		var y = evn.clientY;
		if (y < 2)
			y = 2;
		if (y > (s[1] - 2))
			y = s[1] - 2;
		var x = evn.clientX;
		if (x < 2)
			x = 2;
		if (x > (s[0] - 2))
			x = s[0] - 2;
		if (this._resize) {
			this._resize.resize(x, y);
			return EventListener.cancelEvent(evn);
		}
	});

});

function ResizeSupport(sensor, box) {
	this.sensor = sensor;
	this.box = box;
	this.sensor._resizeSupport = this;
	new EventListener(this.sensor, "mousedown", function(evn) {
		var s = this._resizeSupport;
		document._resize = s;
		s._oldX = evn.clientX;
		s._oldY = evn.clientY;
		return EventListener.cancelEvent(evn);
	});
}

ResizeSupport.prototype.resize = function(evnX, evnY) {
	if (this.box.resize) {
		this.box.resize(evnX - this._oldX, evnY - this._oldY);
	}
	else {
		var w = evnX - this._oldX + this.box.offsetWidth;
		var h = evnY - this._oldY + this.box.offsetHeight;
		this.box.style.height = ((h > 0) ? h : 1) + "px";
		this.box.style.width = ((w > 0) ? w : 1) + "px";
	}
	this._oldX = evnX;
	this._oldY = evnY;
}

function FadeSupport(dom) {
	this.dom = dom;
}

FadeSupport.prototype.show = function() {
	this._clearTimer();
	if (null == this.dom)
		return false;
	this.dom.style.display = "block";
	Dom(this.dom).setOpacity(1);
}

FadeSupport.prototype.hide = function() {
	this._clearTimer();
	if (null == this.dom)
		return false;
	this.dom.style.display = "none";
}

FadeSupport.prototype.fadeOut = function(slow, cleanup) {
	if (this.dom.style.display == "none")
		return;
	this._val = 1;
	this._dec = (slow) ? 0.1 : 0.25;
	if (this.timerId)
		window.clearInterval(this.timerId);
	this.timerId = window.setInterval(function(f) {
		f._val -= f._dec;
		if (f._val > 0.1) {
			Dom(f.dom).setOpacity(f._val);
		}
		else {
			f._clearTimer();
			if (cleanup) {
				f.dom.parentNode.removeChild(f.dom);
				f.dom = null;
			}
			else {
				f.dom.style.display = "none";
				Dom(f.dom).setOpacity(1);
			}
		}
	}, 50, this);
}

FadeSupport.prototype._clearTimer = function() {
	if (this.timerId) {
		var id = this.timerId;
		this.timerId = null;
		window.clearTimeout(id);
	}
}

// Window Panel
// A floating layer panel within the Web page. It can be moved by dragging the
// header.
function WindowPanel(id, doc) {
	WindowPanel._super.call(this, doc.doc.createElement("div"));
	this.id = id;
	this.doc = doc;
	this.element.className = "windowPanel";
	// header
	this.header = this.doc.doc.createElement("div");
	this.header.className = "wnd_header";
	this.header.style.cursor = "move";
	new E.DragMoveSource(this, this.header);
	this.element.appendChild(this.header);
	this.element._panel = this;
	// resize
	var handle = this.doc.doc.createElement("div");
	handle.className = "wnd_resize";
	handle.innerHtml = "&#160;";
	this.element.appendChild(handle);
	new ResizeSupport(handle, this);
	this.hide();
	this.isOpen = false;
	this.isModal = false;
	this.isRememberPosition = true;
	this.doc.body.appendChild(this.element);
}
Object.extend("WindowPanel", "E.Dom");

WindowPanel.prototype._init = function() {
	if (this._initialized)
		return;
	this._initialized = true;
	if (this.isRememberPosition) {
		this._cookie = new Cookie("wnd_" + this.id);
		var ci = this._cookie.getValue();
		if (null != ci) {
			var vals = ci.split(' ');
			this.element.style.left = vals[0] + "px";
			this.element.style.top = vals[1] + "px";
			this.element.style.width = vals[2] + "px";
		}
	}
}

// show/hide is for temporary hiding without the content addressed.
// open/close is for a content based buildup/cleanup. open/close includes
// show/hide.

WindowPanel.prototype.open = function() {
	this.isOpen = true;
	this._init();
	if (this.isModal)
		this.doc.disablePanel.open();
	this.show();
}

WindowPanel.prototype.close = function() {
	this.isOpen = false;
	if (this.isModal)
		this.doc.disablePanel.close();
	this.hide();
}

WindowPanel.prototype.setModal = function(val) {
	this.isModal = val;
	if (this.isModal) {
		if (!this.doc.disablePanel)
			this.doc.disablePanel = new DisablePanel(this.doc);
	}
	if (this.doc.disablePanel) {
		if (this.isOpen && this.isModal)
			this.doc.disablePanel.open();
		else
			this.doc.disablePanel.close();
	}
}

// show or hide
WindowPanel.prototype.display = function(show, displayStyle) {
	if (show && !this.isOpen)
		return;
	WindowPanel._super.prototype.display.call(this, show, displayStyle);
}

WindowPanel.prototype.moveTo = function(v) {
	WindowPanel._super.prototype.moveTo.call(this, v);
	this.rememberPosition();
}

WindowPanel.prototype.resize = function(diffX, diffY) {
	var w = diffX + this.element.offsetWidth;
	var h = diffY + this.element.offsetHeight;
	this.element.style.width = ((w > 0) ? w : 1) + "px";
	this.element.style.height = ((h > 0) ? h : 1) + "px";
	this.rememberPosition();
}

WindowPanel.prototype.rememberPosition = function() {
	if (this._cookie) {
		var s = this.getSize();
		var v = this.getVector();
		this._cookie.setValue(v[0] + " " + v[1] + " " + s[0]);
	}
}

function DisablePanel(doc) {
	DisablePanel._super.call(this, doc.doc.createElement("div"));
	this.doc = doc;
	this.element.id = "_disable";
	this.setOpacity(0.8);
	this.doc.body.appendChild(this.element);
}
Object.extend("DisablePanel", "E.Dom");

DisablePanel.prototype.open = function() {
	this.doc.body.style.overflow = "hidden";
	this.show();
}

DisablePanel.prototype.close = function() {
	this.doc.body.style.overflow = "auto";
	this.hide();
}

// Context Panel
// TODO 9: extend E.Dom
// Only one context panel can be displayed at one time. A click anywhere in the
// document
// automatically closes the panel. If a click should not close the panel, it
// need to be
// canceled before it bubbles to the handler of the panel.
// Base for context menus, date/time pickers,....
// The content for such a panel should provide the following interface:
// show = function(containerDom) { ...add the content dom to the container... };
// clear = function() { ...cleanup all the references (to avoid memory leaks)...
// };
function ContextPanel(content) {
	this.setContent(content);
	this.doc = Doc();
}

// Display the panel.
// Any click on the document will automatically close, hide this context panel.
// @param container The container DOM element to show the panel in.
// @param posX The X position to move the panel to (top/left corner) before
// display.
// @param posY The Y position to move the panel to (top/left corner) before
// display.
// @param evn The event triggering the context panel to show (used to mark click
// to avoid closing in the same event)
ContextPanel.prototype.show = function(container, posX, posY, evn) {
	this.hide();
	while ((container != null)
	    && ((container.namespaceURI == "http://www.w3.org/2000/svg") || (container.nodeName
	        .toLowerCase() == "img")))
		container = container.parentNode;
	if (container == null)
		return;
	this.dom = document.createElement("div");
	this.dom.className = "contextPanel";
	container.appendChild(this.dom);
	if (posX) {
		var p = (this.dom.offsetParent) ? Dom(this.dom.offsetParent).getVector()
		    : [ 0, 0 ];
		var s = Dom(this.dom).getScrollVector();
		var wnd = Wnd(this.dom);
		var v = wnd.getInnerSize();
		var x = posX - p[0] + s[0];
		var w = v[0];
		if ((x - s[0]) > (w - 100)) {
			// the inner window size in Firefox is with scrollbar width, we need to
			// correct
			var sbCorrections = [ 0, 0 ];
			if (navigator.userAgent.indexOf("MSIE") < 0)
				sbCorrections = Dom(this.dom).getScrollbarTotalSize();
			// we cannot use this.dom.offsetWidth to position left, because the
			// content might not yet have been loaded.
			this.dom.style.right = (w - x - sbCorrections[0]) + "px";
		}
		else {
			this.dom.style.left = x + "px";
		}
		var y = posY - p[1];
		var h = v[1];
		if ((y - s[1]) > (h - 100))
			// we cannot use this.dom.offsetHeight to position top, because the
			// content might not yet have been loaded.
			this.dom.style.bottom = (h - y) + "px";
		else
			this.dom.style.top = y + "px";
		this.posX = posX;
		this.posY = posY;
	}
	if (this.content && this.content.show)
		this.content.show(this.dom);
	if (this.doc.contextPanel)
		this.doc.contextPanel.hide();
	// TODO: resign with "blur" event (try to get rid of "ignoreInitialClick"...)
	new EventListener(this.dom, "blur", function(evn) {
		ContextPanel.clear();
	});
	new EventListener(this.dom, "click", function(evn) {
		ContextPanel.clear();
		return EventListener.cancelEvent(evn);
	});
	new EventListener(this.dom, "mousedown", function(evn) {
		evn.stopPropagation();
	});
	if (evn)
		evn._e_contextPanel_open_event = true;
	this.doc.contextPanel = this;
	if (!this.doc.contextPanelClickListener) {
		this.doc.contextPanelClickListener = this.doc.addEventListener("click",
		    function(evn) {
			    if (this.contextPanel && !evn._e_contextPanel_open_event)
				    ContextPanel.clear();
		    });
		this.doc.addEventListener("keydown", function(evn) {
			if (evn.keyCode == 27) // ESC
				  ContextPanel.clear();
		  });
		new EventListener(document, "scroll", ContextPanel.clear);
		new EventListener(window, "resize", ContextPanel.clear);
	}
}

ContextPanel.prototype.hide = function() {
	if (this.content && this.content.clear)
		this.content.clear();
	if (this.dom && this.dom.parentNode)
		this.dom.parentNode.removeChild(this.dom);
	this.dom = null;
	if (typeof (this.onHide) == "function")
		this.onHide();
}

ContextPanel.prototype.isVisible = function() {
	return this.dom != null;
}

ContextPanel.prototype.setContent = function(content) {
	if (this.content) {
		this.content._contextPanel = null;
		this.content.onClose = null;
	}
	this.content = content;
	if (this.content) {
		this.content._contextPanel = this;
		this.content.onClose = function() {
			this._contextPanel.hide();
		};
		if (this.dom && this.content.show)
			this.content.show(this.dom);
	}
}

ContextPanel.clear = function() {
	var doc = Doc();
	if (doc.contextPanel) {
		doc.contextPanel.hide();
		doc.contextPanel = null;
	}
}

// Context menu
function Menu(actionOrLabel) {
	if (!actionOrLabel)
		return;
	this.label = (actionOrLabel.label) ? actionOrLabel.label : actionOrLabel;
	this.action = (actionOrLabel.perform) ? actionOrLabel : null;
}

Menu.prototype.add = function(menu) {
	// TODO: handle and display submenus (so far only one-level menus are
	// supported)
	if (!this.subMenus)
		this.subMenus = new Array();
	this.subMenus.push(menu);
	menu._parent = this;
	if (1 < this.subMenus.length) {
		var p = this.subMenus[this.subMenus.length - 2];
		p._next = menu;
		menu._previous = p;
	}
};

Menu.prototype._appendToDom = function(box) {
	this.dom = document.createElement("div");
	this.dom.className = "mn_menu";
	if (this.action && this.action.disabled)
		this.dom.className = "mn_menu_disabled";
	var lb = document.createElement("div");
	lb.className = "mn_label";
	lb.innerHTML = this.label;
	this.dom.appendChild(lb);
	box.appendChild(this.dom);
	this.dom._menu = this;
	new EventListener(this.dom, "mouseover", function(evn) {
		this._menu.focus();
		return EventListener.cancelEvent(evn);
	});
	new EventListener(this.dom, "mouseout", function(evn) {
		this._menu.blur();
		return EventListener.cancelEvent(evn);
	});
	new EventListener(this.dom, "click", function(evn) {
		if (this._menu.action && this._menu.action.disabled)
			return EventListener.cancelEvent(evn);
		this._menu.perform();
	});
};

Menu.prototype.show = function(containerDom) {
	if (this.box) {
		this.box.style.display = "block";
	}
	else if (this.subMenus && containerDom) {
		var doc = containerDom.ownerDocument;
		this.box = doc.createElement("div");
		this.box.className = "mn_box";
		if (this._parent) {
			var helper = doc.createElement("div");
			helper.style.position = "absolute";
			helper.style.top = "0px";
			helper.style.left = (this.dom) ? (this.dom.offsetWidth) + "px" : "0px";
			containerDom.appendChild(helper);
			helper.appendChild(this.box);
		}
		else {
			containerDom.appendChild(this.box);
		}
		for ( var i = 0; i < this.subMenus.length; ++i)
			this.subMenus[i]._appendToDom(this.box);
		if (!doc._contextMenuKeyListener) {
			doc._contextMenuKeyListener = new EventListener(doc, "keydown", function(
			    evn) {
				if (!this._currentMenuFocus)
					return;
				var m = this._currentMenuFocus;
				if (evn.keyCode == 13) // enter
					  m.perform();
				  else if (evn.keyCode == 40) // arrow down
					  (m._next) ? m._next.focus() : (m._parent) ? m._parent.subMenus[0]
					      .focus() : m.subMenus && m.subMenus[0].focus();
				  else if (evn.keyCode == 39) // arrow right
					  m.subMenus && m.subMenus[0].focus();
				  else if (evn.keyCode == 38) { // arrow up
					  if (m._previous)
						  m._previous.focus();
					  else {
						  var sMs = (m._parent) ? m._parent.subMenus : m.subMenus;
						  sMs && sMs[sMs.length - 1].focus();
					  }
				  }
				  else if (evn.keyCode == 37) // arrow left
					  m._parent && m._parent._parent && m._parent.focus();
				  else
					  return;
				  return EventListener.cancelEvent(evn);
			  });
		}
		doc._currentMenuFocus = this;
	}
}

Menu.prototype.hide = function() {
	if (this.box)
		this.box.style.display = "none";
}

Menu.prototype.focus = function() {
	var doc = (this.dom) ? this.dom.ownerDocument : this.box.ownerDocument;
	if ((doc._currentMenuFocus) && (doc._currentMenuFocus != this._parent))
		doc._currentMenuFocus.blur();
	doc._currentMenuFocus = this;
	if (!this.dom)
		return;
	Dom(this.dom).addStyleClass("mn_over");
	this.dom.focus();
	this.show(this.dom);
}

Menu.prototype.blur = function() {
	var doc = (this.dom) ? this.dom.ownerDocument : this.box.ownerDocument;
	if (doc._currentMenuFocus == this)
		doc._currentMenuFocus = this._parent;
	if (!this.dom)
		return;
	Dom(this.dom).removeStyleClass("mn_over");
	this.hide();
}

Menu.prototype.perform = function() {
	if (this.action && this.action.disabled)
		return;
	ContextPanel.clear();
	if (this.action)
		this.action.perform();
}

Menu.prototype.clear = function() {
	if (this.dom || this.box) {
		var doc = (this.dom) ? this.dom.ownerDocument : this.box.ownerDocument;
		if (doc._currentMenuFocus == this)
			doc._currentMenuFocus = null;
	}
	if (this.subMenus) {
		for ( var i = 0; i < this.subMenus.length; ++i)
			this.subMenus[i].clear();
	}
	if (this.dom && this.box)
		this.dom.removeChild(this.box);
	this.dom = null;
	this.box = null;
}

// Date/Time Picker
function DatePicker(containerDom, input, evn) {
	this.input = input;
	this.selDate = Date.parseXsDate(this.input.value);
	if (!this.selDate)
		this.selDate = new Date();
	var cp = new ContextPanel(this);
	var p = Dom(containerDom).getVector();
	cp.show(containerDom.parentNode, p[0] + 6, p[1] + 6, evn);
}

DatePicker.prototype._buildDom = function() {
	this.dom = document.createElement("table");
	this.dom.cellSpacing = 0;
	this.dom.className = "DT_picker";
	this.dom._picker = this;
	new EventListener(this.dom, "click", EventListener.cancelEvent);
	var mouseWheelHandler = function(evn) {
		var m = this._picker.selDate.getMonth() + ((evn.detail > 0) ? 1 : -1);
		this._picker.selDate = new Date(Date.UTC(
		    this._picker.selDate.getFullYear(), m, 1));
		this._picker._fill();
		return EventListener.cancelEvent(evn);
	};
	new EventListener(this.dom, "DOMMouseScroll", mouseWheelHandler);
	new EventListener(this.dom, "mousewheel", mouseWheelHandler);
	var t = Dom(this.dom).createChild("tbody");
	var tr = Dom(t).createChild("tr");
	this._info = Dom(tr).createChild("th");
	this._info.colSpan = 7;
	this._info.className = "info";
	this._info.show = function(date) {
		this.innerHTML = date.toLocaleDateString();
	}
	var c = Dom(tr).createChild("th", "X");
	c.className = "close";
	c._picker = this;
	new EventListener(c, "click", function(evn) {
		this._picker.onClose();
	});
	var tr = Dom(t).createChild("tr");
	var todayCell = Dom(tr).createChild("th");
	todayCell.colSpan = 2;
	todayCell.style.textAlign = "left";
	var today = Dom(todayCell).createChild("img");
	today.src = BasePath.get() + "/livcos.org/web/img/today.gif";
	today.alt = "today";
	today._picker = this;
	new EventListener(today, "click", function(evn) {
		this._picker.selDate = new Date();
		this._picker.onCommit();
	});
	var s = Dom(tr).createChild("th");
	s.colSpan = 6;
	this.month = Dom(s).createChild("select");
	this.month._picker = this;
	for ( var i = 0; i < Date.MONTH_NAMES.length; ++i)
		Dom(this.month).createChild("option", Date.MONTH_NAMES[i]).value = i;
	this.month.options[this.selDate.getMonth()].selected = true;
	new EventListener(this.month, "change", function(evn) {
		this._picker.selDate = new Date(Date.UTC(
		    this._picker.selDate.getFullYear(), this.value, 1));
		this._picker._fill();
	});
	new EventListener(this.month, "blur", EventListener.cancelEvent);
	this.year = Dom(s).createChild("select");
	this.year._picker = this;
	for ( var i = 0; i < 10; ++i)
		Dom(this.year).createChild("option");
	this.year.setDate = function(date) {
		var y = date.getFullYear() - 4;
		for ( var i = 0; i < 10; ++i) {
			this.options[i].innerHTML = y + i;
			this.options[i].value = y + i;
		}
		this.options[4].selected = true;
	};
	new EventListener(this.year, "change", function(evn) {
		this._picker.selDate = new Date(Date.UTC(this.value, this._picker.selDate
		    .getMonth(), 1));
		this._picker._fill();
		this.setDate(this._picker.selDate);
	});
	new EventListener(this.year, "blur", EventListener.cancelEvent);
	this.year.setDate(this.selDate);
	var tr = Dom(t).createChild("tr");
	Dom(tr).createChild("th", "wk");
	for ( var d = 0; d < 7; ++d)
		Dom(tr).createChild("th",
		    Date.DAY_NAMES[(6 == d) ? 0 : d + 1].substring(0, 2));
	for ( var w = 0; w < 6; ++w) {
		var tr = Dom(t).createChild("tr");
		Dom(tr).createChild("th");
		for ( var d = 0; d < 7; ++d) {
			var td = Dom(tr).createChild("td");
			td._picker = this;
			new EventListener(td, "mouseover", function(evn) {
				this._picker._info.show(this._date);
				this._originalClass = this.className;
				this.className = this._originalClass + " hover";
			});
			new EventListener(td, "mouseout", function(evn) {
				this.className = this._originalClass;
			});
			new EventListener(td, "click", function(evn) {
				this._picker.selDate = this._date;
				this._picker.onCommit();
			});
		}
	}
};

DatePicker.prototype._fill = function() {
	var firstDate = new Date(Date.UTC(this.selDate.getFullYear(), this.selDate
	    .getMonth(), 1));
	var dayOffset = firstDate.getDay() - 1;
	if (0 > dayOffset)
		dayOffset = 6;
	var offset = firstDate.getTime() - dayOffset * Date.ONE_DAY;
	var weekNum = firstDate.getWeekNumber();
	for ( var w = 0; w < 6; ++w) {
		var tr = this.dom.firstChild.childNodes[w + 3];
		tr.childNodes[0].innerHTML = weekNum + w;
		for ( var d = 0; d < 7; ++d) {
			var date = new Date(offset + ((w * 7) + d) * Date.ONE_DAY);
			var day = date.getUTCDate();
			var td = tr.childNodes[d + 1];
			td.innerHTML = day;
			if ((date < firstDate) || ((3 < w) && (20 > day)))
				td.className = "blur";
			else
				td.className = "";
			td._date = date;
		}
	}
	this._info.show(this.selDate);
	this.month.options[this.selDate.getMonth()].selected = true;
	this.year.setDate(this.selDate);
};

DatePicker.prototype.onCommit = function() {
	this.input.value = this.selDate.toXsDateString();
	this.onClose();
};

DatePicker.prototype.onClose = function() {
	alert("close");
};

DatePicker.prototype.show = function(containerDom) {
	this._buildDom();
	containerDom.appendChild(this.dom);
	this._fill();
};

DatePicker.prototype.clear = function() {
	if (this.dom)
		this.dom.parentNode.removeChild(this.dom);
	this.dom = null;
};

function DateInput(input) {
	this.input = input;
	var c = Dom(this.input.parentNode).createChild("span");
	c.className = "DT_input";
	// create inputs (should work also in IE)
	// overflow:auto div to work around input cursor bug in Firefox
	c.innerHTML = "<div style='overflow: auto; float: left;'><input type='text'/></div><img/>";
	this.dateInput = c.firstChild.firstChild;
	DateInput._setup(this, this.dateInput, c.childNodes[1], this.input.value);
	this.input.style.display = "none";
}

DateInput._setup = function(dateInput, inpt, btn, value) {
	inpt.value = value;
	inpt.size = 10;
	inpt.style.width = "auto";
	inpt._dti = dateInput;
	inpt.onchange = function() {
		var d = Date.parseXsDate(this.value);
		if (!d)
			d = new Date(this.value);
		this._dti.setDate(d);
	};
	btn.src = BasePath.get() + "/livcos.org/web/img/calendar.gif";
	btn.alt = "...";
	btn._dti = dateInput;
	new EventListener(btn, "click", function(evn) {
		var dp = new DatePicker(this, this._dti.dateInput, evn);
		dp._dti = this._dti;
		dp.onCommit = function() {
			this._dti.setDate(this.selDate);
			this.onClose();
		};
	});
}

DateInput.prototype.setDate = function(date) {
	var val = "";
	if (date && (date.getTime() > 0))
		val = date.toXsDateString();
	this.dateInput.value = val;
	this.input.value = val;
};

function DateTimeInput(input) {
	this.input = input;
	var c = Dom(this.input.parentNode).createChild("span");
	c.className = "DT_input";
	// create inputs (should work also in IE)
	// overflow:auto div to work around input cursor bug in Firefox
	c.innerHTML = "<div style='overflow: auto; float: left;'><input type='text'/></div><div style='float: left;'><img/></div>"
	    + "<div style='overflow: auto; float: left;'><input type='text'/></div><input type='button'/>";
	var tockens = this.input.value.split("T");
	this.dateInput = c.firstChild.firstChild;
	DateInput
	    ._setup(this, this.dateInput, c.childNodes[1].firstChild, tockens[0]);
	this.timeInput = c.childNodes[2].firstChild;
	var val = tockens[1];
	if (val) {
		var commaPos = val.indexOf(".");
		if (commaPos > 0)
			val = val.substring(0, commaPos);
		this.timeInput.value = val;
	}
	this.timeInput.size = 8;
	this.timeInput.style.width = "auto";
	this.timeInput._dti = this;
	this.timeInput.onchange = function() {
		this._dti.setTime(Date.parseXsTime(this.value));
	};
	var bn_now = c.childNodes[3];
	bn_now.value = "now";
	bn_now._dti = this;
	bn_now.onclick = function() {
		this._dti.setTime(new Date());
	};
	this.input.style.display = "none";
}

DateTimeInput.prototype.setDate = function(date) {
	var val = "";
	if (date && (date.getTime() > 0))
		val = date.toXsDateString();
	this.dateInput.value = val;
	var tockens = this.input.value.split("T");
	this.input.value = val + "T" + tockens[1];
};

DateTimeInput.prototype.setTime = function(time) {
	var val = "";
	if (time && (time.getTime() > 0))
		val = time.toXsTimeString();
	this.timeInput.value = val;
	var tockens = this.input.value.split("T");
	this.input.value = tockens[0] + "T" + val;
};

/**
 * @class Add the auto-complete feature to a form text input.
 * @param {text
 *          input} dom The form text input to mask with the feature.
 * @param {string[]}
 *          values Array of string values for the selection list.
 * @constructor
 */
function AutoCompleteInput(dom, values) {
	this.dom = dom;
	this.values = values;
	this.dom._ac = this;
	// switch off browser autocomplete
	this.dom.setAttribute("autocomplete", "off");
	this.listDom = this.dom.ownerDocument.createElement("div");
	this.listDom.className = "auto_complete";
	this.dom.parentNode.insertBefore(this.listDom, this.dom.nextSibling);
	new EventListener(this.dom, "blur", function(evn) {
		Wnd(this).wnd.setTimeout(function(ac) {
			ac.hide();
		}, 500, this._ac);
	});
	new EventListener(this.dom, "keydown", function(evn) {
		if (!evn.ctrlKey && !evn.shiftKey && !evn.altKey && (evn.keyCode == 13)) { // Enter
			  if (this._ac.canSelect()) {
				  this._ac.select();
				  return EventListener.cancelEvent(evn);
			  }
		  }
		  else if (evn.keyCode == 27) { // ESC
			  if (this._ac.listDom.style.display != "none") {
				  this._ac.hide();
				  return EventListener.cancelEvent(evn);
			  }
		  }
		  else if (evn.ctrlKey && evn.shiftKey && (evn.keyCode == 83)) { // s
			  this._ac.sort();
		  }
	  });
	new EventListener(this.dom, "keyup", function(evn) {
		var ac = this._ac;
		if (evn.keyCode == 40) { // arrow down
			  if (ac._current) {
				  if (ac._current.nextSibling)
					  ac.choose(ac._current.nextSibling, true);
				  else
					  ac.choose(ac.listDom.firstChild, true);
			  }
			  else {
				  ac.choose(ac.listDom.firstChild, true);
			  }
		  }
		  else if (evn.keyCode == 38) { // arrow up
			  if (ac._current) {
				  if (ac._current.previousSibling)
					  ac.choose(ac._current.previousSibling, true);
				  else
					  ac.choose(ac.listDom.lastChild, true);
			  }
		  }
		  else if ((evn.keyCode != 39) && (evn.keyCode != 37)) {
			  // any key that could change the value (all except arrow keys)
			ac.process(this.value);
		}
	});
}

AutoCompleteInput.prototype.process = function(value) {
	if (value == "") {
		this.hide();
		this.choose();
	}
	else if (value != this._oldVal) {
		this.readValues(value);
	}
	this._oldVal = value;
}

AutoCompleteInput.prototype.choose = function(item, scrollIntoView) {
	if (this._current)
		this._current.className = "ac_item";
	this._current = null;
	if (item) {
		this._current = item;
		this._current.className = "ac_choose";
		if (scrollIntoView)
			this._current.scrollIntoView(false);
	}
}

AutoCompleteInput.prototype.canSelect = function() {
	return this._current && (this.listDom.style.display != "none");
}

AutoCompleteInput.prototype.select = function(item) {
	var itm = (item) ? item : this._current;
	this.dom.value = itm.firstChild.nodeValue;
	this._oldVal = this.dom.value;
	this.hide();
	this.onSelect && this.onSelect(itm);
}

AutoCompleteInput.prototype.readValues = function(str) {
	this.update(this.values);
}

AutoCompleteInput.prototype.matchValue = function(value, str) {
	return value.toLowerCase().indexOf(str.toLowerCase()) >= 0;
}

AutoCompleteInput.prototype.update = function(values) {
	this.choose();
	this.listDom.innerHTML = "";
	var matches = new Array();
	if (values) {
		for ( var i = 0; i < values.length; ++i) {
			if (this.matchValue(values[i], this.dom.value)) {
				matches[matches.length] = values[i];
				if (this.withDetails)
					matches[matches.length] = values[i + 1];
			}
			this.withDetails && i++;
		}
	}
	for ( var i = 0; i < matches.length; ++i) {
		var item = this.listDom.ownerDocument.createElement("div");
		item._ac = this;
		item._idx = i;
		item.className = "ac_item";
		item.innerHTML = matches[i];
		if (this.withDetails) {
			item._details = matches[++i].replace("&", "&amp;").replace("<", "&lt;")
			    .replace(">", "&gt;");
			item.innerHTML += "<div class=\"ac_details\">" + item._details + "</div>";
		}
		this.listDom.appendChild(item);
		item.onmouseover = function() {
			this._ac.choose(this);
		};
		item.onclick = function() {
			this._ac.select(this);
		};
	}
	if (matches.length <= 0)
		this.hide();
	else
		this.show();
}

AutoCompleteInput.prototype.show = function() {
	this.listDom.style.display = "block";
	this.listDom.style.top = this.dom.parentNode.offsetHeight + "px";
	this.choose(this.listDom.firstChild);
}

AutoCompleteInput.prototype.hide = function() {
	this.choose();
	this.listDom.style.display = "none";
}

AutoCompleteInput.prototype.sort = function() {
	this.sortDetails = !this.sortDetails;
	var strs = new Array();
	for ( var i = 0; i < this.listDom.childNodes.length; ++i) {
		var n = this.listDom.childNodes[i];
		strs[i] = n;
	}
	strs.sort(function(a, b) {
		return (a._ac.sortDetails) ? a._details > b._details : a._idx > b._idx;
	});
	for ( var i = 0; i < strs.length; ++i)
		this.listDom.appendChild(strs[i]);
}

/**
 * @class Add the LivCos reference/URL auto-complete feature to a form text
 *        input.
 * @param {text
 *          input} dom The form text input to mask with the feature.
 * @constructor
 */
function RefAutoCompleteInput(dom, objId) {
	RefAutoCompleteInput._super.call(this, dom);
	this.withDetails = true;
	this.objId = objId;
	var i = objId.lastIndexOf("/");
	this.baseObjId = objId.substring(0, i);
	this.lists = new Object();
	RefAutoCompleteInput.WEB_PROTOCOLS = [ "http://", "Web Page", "https://",
	    "Secure Web Page" ];
}
Object.extend("RefAutoCompleteInput", "AutoCompleteInput");

RefAutoCompleteInput.prototype.onSelect = function(item) {
	if (item._details && (item._details.indexOf("child object") > 0))
		this.dom.value += "/";
}

RefAutoCompleteInput.prototype.readValues = function(str) {
	for ( var i = 0; i < RefAutoCompleteInput.WEB_PROTOCOLS.length; ++i) {
		if (str.indexOf(RefAutoCompleteInput.WEB_PROTOCOLS[i++]) == 0)
			return;
	}
	var pfx = "";
	var isEntity = false;
	var idx = str.lastIndexOf("/");
	if (idx >= 0)
		pfx = str.substring(0, idx + 1);
	idx = str.indexOf("#");
	if (idx >= 0) {
		pfx = str.substring(0, idx + 1);
		isEntity = true;
	}
	this.lastPrefix = pfx;
	var list = this.lists[pfx];
	if (!list) {
		var l = [ pfx, '...loading...' ];
		this.lists[pfx] = l;
		this.update(l);
		var objId = (pfx == "/") ? pfx : pfx.substring(0, pfx.length - 1);
		if (objId == "")
			objId = (isEntity) ? this.objId : this.baseObjId;
		else if (objId.indexOf("/") != 0)
			objId = this.baseObjId + "/" + objId;
		var url = BasePath.get() + "/livcos.org/web/json/refs.view?ref=" + objId
		    + "&entities=" + ((isEntity) ? "1" : "0");
		var r = new LivCosResource(url, "");
		r._ac = this;
		r._pfx = pfx;
		r.onSuccess = function() {
			var l = Resource.parseJson(this.text);
			if (l.error) {
				this.statusLog.showError("Failed to load auto-complete references: " + l.message);
			}
			else if (!l || !l.length) {
				this.statusLog.showError("Failed to load auto-complete references!");
			}
			else {
				this.statusLog.show("...references loaded.");
				if (this._pfx != "") {
					for ( var i = 0; i < l.length; i += 2) {
						l[i] = this._pfx + l[i];
					}
				}
				else {
					for ( var i = 0; i < RefAutoCompleteInput.WEB_PROTOCOLS.length; ++i)
						l[l.length] = RefAutoCompleteInput.WEB_PROTOCOLS[i];
				}
			}
			this._ac.lists[this._pfx] = l;
			if (this._pfx == this._ac.lastPrefix)
				this._ac.update(l);
		}
		r.load();
	}
	else {
		this.update(list);
	}
}

RefAutoCompleteInput.prototype.matchValue = function(value, str) {
	return value.toLowerCase().indexOf(str.toLowerCase()) >= 0;
}

E.Dom.prototype.setRefAutoComplete = function(objId) {
	return new RefAutoCompleteInput(this.element, objId);
}

/**
 * @class Inline edit feature.
 * @param {element}
 *          dom The DOM element to allow to edit the content for.
 * @param {function}
 *          saveFn The callback function when the user wants to save the changed
 *          content.
 * @constructor
 */
function InlineEdit(dom, saveFn, emptyPlaceholder) {
	this.dom = dom;
	this.saveFn = saveFn;
	this.emptyPlaceholder = emptyPlaceholder;
	var pathSpec = this.dom.getFirstChildByClassName("inlnEd_path");
	if (pathSpec) {
		this.valuePath = pathSpec.title;
		pathSpec.parentNode.removeChild(pathSpec);
	}
	// this.dom.element.setAttribute("tabIndex", "1");
	this.dom.addStyleClass("inlineEdit");
	this._content = this.dom.serializeContent();
	if ((this._content == "") && this.emptyPlaceholder) {
		this._content = this.emptyPlaceholder;
		this.dom.element.innerHTML = this._content;
	}
	if (this.emptyPlaceholder == this._content)
		this.dom.addStyleClass("inlnEd_inactive");
	this.dom.element._edit = this;
	if (typeof this.dom.element.contentEditable != "undefined") {
		new EventListener(this.dom.element, "click", function(evn) {
			if (!Doc(this).inlineEdit.active)
				return;
			if (this._rightClick)
				return;
			if (evn.shiftKey) {
				this.contentEditable = true;
				this.focus();
			}
		});
		new EventListener(this.dom.element, "dblclick", function(evn) {
			if (!Doc(this).inlineEdit.active)
				return;
			if (this._rightClick)
				return;
			if (evn.target.localName && (evn.target.localName.toLowerCase() == "a"))
				// ignore inline edits on links
				return;
			if (window.getSelection) {
				var sel = Wnd(this).wnd.getSelection();
				var r = null;
				if (0 < sel.rangeCount)
					r = sel.getRangeAt(0);
				this.contentEditable = true;
				this.focus();
				if (r) {
					sel.removeAllRanges();
					sel.addRange(r);
				}
			}
			else {
				var sel = this.ownerDocument.selection;
				var r = sel.createRange();
				this.contentEditable = true;
				this.focus();
				r.select();
			}
		});
		new EventListener(this.dom.element, "mousedown", function(evn) {
			this._rightClick = evn.button == 2;
		});
		new EventListener(this.dom.element, "focus", function(evn) {
			if (!Doc(this).inlineEdit.active)
				return;
			if (this._rightClick)
				return;
			this._edit.enter();
		});
		new EventListener(this.dom.element, "blur", function(evn) {
			if (this._rightClick)
				return;
			this._edit.leave();
			this.contentEditable = false;
		});
		new EventListener(this.dom.element, "keydown", function(evn) {
			if (evn.keyCode == 27) { // ESC
			  this._edit.reset();
			  this.blur();
		  }
		  else if (evn.ctrlKey && !evn.altKey && (evn.keyCode == 13)) { // Ctrl-Enter
			  this.blur();
		  }
		  else if (evn.ctrlKey && !evn.altKey && (evn.keyCode == 86)) { // Ctrl-V
			  if (this.localName != "div") {
				  // FF 3 seems to insert, paste rich-text to break out of this
				  // container. Since we don't know what gets pasted, we need to
					// suppress it completely. It works within <div> containers.
			  	var nextSibling = this.nextSibling;
			  	window.setTimeout(function (container, originalSibling) {
			  		var doc = container.ownerDocument;
			  		var n = container.nextSibling;
			  		var p = container.parentNode;
			  		while ((n != originalSibling) && (null != n)) {
			  			container.appendChild(doc.createTextNode(n.textContent));
			  			var tmp = n.nextSibling;
			  			p.removeChild(n);
			  			n = tmp;
			  		}
			  	}, 500, this, this.nextSibling);
			  }
		  }
		  else if (evn.keyCode == 13) { // Enter
			  if (this.localName == "span") {
				  // suppress Enter in inline elements
				  return EventListener.cancelEvent(evn);
			  }
			  else if (!evn.shiftKey && (this.localName != "div")) {
			  	StatusDisplay.getInstance().createLog(3000).show("Enter key is not supported! Please use Shift-Enter instead.");
				  return EventListener.cancelEvent(evn);
			  }
		  }
		  return InlineEdit.onRichTextKey(evn);
	  });
	}
	else {
		new EventListener(this.dom.element, "dblclick", function(evn) {
			if (!Doc(this).inlineEdit.active)
				return;
			Dom(this).addStyleClass("inlnEd_active");
			this._edit._iframeEdit();
		});
	}
}

InlineEdit.prototype.enter = function() {
	this.dom.removeStyleClass("inlnEd_inactive");
	this.dom.addStyleClass("inlnEd_active");
	var doc = Doc(this.dom);
	if ((this.emptyPlaceholder == this._content)
	    || (0 == this._content.indexOf("--")))
		doc.doc.execCommand("selectAll", false, null);
	// not supported in IE
	if (browser.isGeko)
		doc.doc.execCommand("styleWithCSS", false, false);
	doc.inlineEdit.focus = this;
}

InlineEdit.prototype.leave = function() {
	this.dom.removeStyleClass("inlnEd_active");
	for ( var i = 0; i < this.dom.element.childNodes.length; ++i) {
		var n = this.dom.element.childNodes[i];
		// TODO: there must be another, more elegant way to ignore blur events after
		// a context menu...
		if (n.className && (n.className.indexOf("contextPanel") >= 0))
			this.dom.element.removeChild(n);
	}
	var c = this.dom.serializeContent();
	if (c != this._content) {
		this.saveFn(c);
		this._content = c;
	}
	if (this.emptyPlaceholder == this._content)
		this.dom.addStyleClass("inlnEd_inactive");
	Doc(this.dom).inlineEdit.focus = null;
}

InlineEdit.prototype.reset = function() {
	this.dom.element.innerHTML = this._content;
}

InlineEdit.prototype._iframeEdit = function() {
	var s = window.getComputedStyle(this.dom.element, "");
	var f = this.dom.element.ownerDocument.createElement("iframe");
	// f.setAttribute("width", "100%");
	f.setAttribute("frameborder", "0", false);
	f.setAttribute("vspace", "0", false);
	f.style.position = s.position;
	f.style.top = s.top;
	f.style.left = s.left;
	f.style.bottom = s.bottom;
	f.style.right = s.right;
	f.style.width = s.width;
	f.style.display = "none";
	f._edit = this;
	this._iframe = f;
	this.wnd = window;
	new EventListener(f, "load", function(evn) {
		this._edit.dom.element.style.display = "none";
		this.style.display = "block";
		var doc = this.contentWindow.document;
		doc.head = doc.documentElement.getElementsByTagName("head")[0];
		var h = "<style type=\"text/css\">";
		h += " body { background-color: #FFFFEE; margin: 0; padding: 0; }";
		h += " a { color: blue; }";
		h += "</style>";
		doc.head.innerHTML = h;
		// copy the most important styles
		  var styles = [ "fontFamily", "fontSize", "fontWeight", "fontStyle",
		      "color", "backgroundColor", "paddingTop", "paddingBottom",
		      "paddingLeft", "paddingRight", "textAlign" ];
		  var s = this._edit.wnd.getComputedStyle(this._edit.dom.element, "");
		  this.style.marginTop = s.marginTop;
		  this.style.marginBottom = s.marginBottom;
		  this.style.marginLeft = s.marginLeft;
		  this.style.marginRight = s.marginRight;
		  for ( var i = 0; i < styles.length; ++i)
			  doc.body.style[styles[i]] = s[styles[i]];
		  doc.body.style.overflow = "hidden";
		  doc.body.innerHTML = this._edit.dom.element.innerHTML;
		  this.style.height = doc.body.offsetHeight + "px";
		  doc.designMode = "on";
		  if (browser.isGeko)
			  doc.execCommand("styleWithCSS", false, false);
		  if ((this._edit.emptyPlaceholder == this._edit._content)
		      || (0 == this._edit._content.indexOf("--")))
			  doc.execCommand("selectAll", false, null);
		  doc._edit = this._edit;
		  new EventListener(doc, "keyup", function(evn) {
			  this._edit._iframe.style.height = this.body.offsetHeight + "px";
		  });
		  doc._wnd = this.contentWindow;
		  new EventListener(doc, "keydown", function(evn) {
			  if (evn.keyCode == 27) { // ESC
				    this.body.innerHTML = this._edit._content;
				    this._wnd.blur();
			    }
			    else if (evn.ctrlKey && !evn.altKey && (evn.keyCode == 13)) { // Ctrl-Enter
				    this._wnd.blur();
			    }
			    return InlineEdit.onRichTextKey(evn);
		    });
		  new EventListener(doc, "blur", function(evn) {
			  var ed = this._edit;
			  ed.dom.element.innerHTML = this.body.innerHTML;
			  ed.wnd.setTimeout(function() {
				  ed.leave();
			  }, 1);
			  ed.dom.element.style.display = "block";
			  ed._iframe.parentNode.removeChild(ed._iframe);
			  ed._iframe = null;
		  });
		  this.contentWindow.focus();
	  });
	this.dom.element.parentNode.insertBefore(f, this.dom.element);
}

InlineEdit.onRichTextKey = function(evn) {
	if (evn.ctrlKey && evn.shiftKey) {
		var doc = evn.target.ownerDocument;
		if (evn.keyCode == 66) // Ctrl-B
			doc.execCommand("bold", false, null);
		else if (evn.keyCode == 73) // Ctrl-I
			doc.execCommand("italic", false, null);
		else if (evn.keyCode == 85) // Ctrl-U
			doc.execCommand("underline", false, null);
		else if (evn.keyCode == 75) // Ctrl-K
			doc.execCommand("strikethrough", false, null);
		else if (evn.keyCode == 76) // Ctrl-L
			doc.execCommand("createlink", false, "#");
		else if (evn.keyCode == 68) // Ctrl-D
			doc.execCommand("subscript", false, null);
		else if (evn.keyCode == 80) // Ctrl-P
			doc.execCommand("superscript", false, null);
		else if (evn.keyCode == 82) // Ctrl-P
			doc.execCommand("removeFormat", false, null);
		else if (evn.keyCode == 78) // Ctrl-N
			doc.execCommand("unlink", false, null);
		else
			doc = null;
		if (doc != null)
			return EventListener.cancelEvent(evn);
	}
}

E.Doc.prototype.inlineEdit = {
  focus : null, // holds the current InlineEdit on focus
  active : false
// activate/deactivate all the documents inline edits
}

E.Dom.prototype.setInlineEdit = function(saveFn, emptyPlaceholder) {
	if (this.inlineEdit) {
		this.inlineEdit.saveFn = saveFn;
		this.inlineEdit.emptyPlaceholder = emptyPlaceholder;
	}
	else {
		this.inlineEdit = new InlineEdit(this, saveFn, emptyPlaceholder);
	}
	return this.inlineEdit;
}

function Tooltip(dom) {
	this.dom = dom;
	this.dom.tooltip = this;
	this.dom.element._tooltip = this;
	this.active = true;
	this.dom.addEventListener("mouseover", function(evn) {
		this.tooltip.show();
	});
	this.dom.addEventListener("mousemove", function(evn) {
		var s = this.getScrollVector();
		this.tooltip.moveTo( [ evn.clientX + s[0] + 8, evn.clientY + s[1] + 2 ]);
	});
	this.dom.addEventListener("mouseout", function(evn) {
		this.tooltip.hide();
	});
	this.dom.addEventListener("mousedown", function(evn) {
		this.tooltip.hide();
	});
	this.dom.addEventListener("mouseup", function(evn) {
		this.tooltip.hide();
	});
}

Tooltip.prototype.show = function() {
	if (this.ttDom || !this.active)
		return;
	var tt = this.dom.getElementsByClassName("tooltip")[0];
	if (!tt)
		return;
	this.ttDom = Dom(tt.cloneNode(true));
	var p = document.body;
	var tts = Dom(p).getChildrenByClassName("tooltip");
	for ( var i = 0; i < tts.length; i++)
		p.removeChild(tts[i]);
	p.appendChild(this.ttDom.element);
}

Tooltip.prototype.moveTo = function(v, anchor) {
	if (!this.ttDom)
		return;
	this.ttDom.moveTo(v, anchor);
	this.ttDom.show("block");
}

Tooltip.prototype.hide = function() {
	if (!this.ttDom)
		return;
	this.ttDom.hide();
	document.body.removeChild(this.ttDom.element);
	this.ttDom = null;
}

E.Dom.prototype.setTooltip = function(active) {
	if (!this.tooltip)
		this.tooltip = new Tooltip(this);
	if (typeof (active) != "undefined")
		this.tooltip.active = active;
	return this.tooltip;
}
